﻿<%
'''核心入口类
Class AshapoSa
	
	'''当前模块
	Private s_module
	
	'''当前控制器
	Private s_controller
	
	'''当前操作
	Private s_action
	
	'''当前主题
	Private s_theme
	
	'''当前视图完整路径
	Private s_view
	
	'''当前视图文件解析后保存的文件名称(不含路径)
	Private s_view_output_file
	
	'''当前文件在解析过程中可能涉及的各include文件检测工具文件
	Private s_view_output_map
	
	'''todo 待删除
	Private s_nowL
	
	'''核心使用的AshapoFso对象
	Private s_fso
	
	'''核心使用的AshapoAsp对象
	Private s_asp
	
	'''核心使用的AshapoConfig对象
	Public Config
	
	'''核心使用的AshapoString对象
	Private s_str
	
	'''(按需加载)核心使用的AshapoJson对象
	Private s_json
	
	'''核心使用的AshapoCache对象
	Public Cache
	
	'''核心使用的AshapoCookie对象
	Public Cookie
	
	'''Sa控制下的控制台，只能有一个实例
	Public Console

	'''Sa控制下的Request，只能有一个实例
	Public [Request]
	
	
	'''构造
	Private Sub Class_Initialize()
		Session.CodePage = 65001
		Response.Charset = "UTF-8"
		
		Set s_str = New AshapoString
		
		Set Config = New AshapoConfig
		
		Set s_asp = New AshapoAsp
		
		Set Console = New AshapoConsole
		
		Set s_fso = New AshapoFso

		Set [Request] = New AshapoRequest
		
	End Sub
	
	'''析构
	Private Sub Class_Terminate()
		
		Set [Request] = Nothing

		Set Cookie = Nothing

		Set Cache = Nothing
		
		Set s_asp = Nothing
		Set s_fso = Nothing
		Set Config = Nothing
		
		If Has(s_json) Then
			Set s_json = Nothing
		End If
		
		If Has(s_cache) Then
			Set s_cache = Nothing
		End If
		
		If Has(s_cookie) Then
			Set s_cookie = Nothing
		End If
		
		Set s_str = Nothing
		
		Set Console = Nothing
	End Sub
	
	''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''核心相关方法
	
	'''上下文无关动态include
	'''由于是上下文无关，故重复include是不会产生效果的
	'p_path:文件路径
	Public Sub Include(Byval p_path)
		s_asp.Include(p_path)
	End Sub
	
	'''获取当前的系统常量
	'''MODULE_NAME:当前模块名
	'''CONTROLLER_NAME:当前控制器名
	'''ACTION_NAME:当前操作名
	'''__SELF__当前URL
	'p_key:设置键名，不区分大小写，但建议都使用大写
	Public Property Get Constant(Byval p_key)
		Select Case Ucase(p_key)
		Case "MODULE_NAME"
			Constant = s_module
		Case "CONTROLLER_NAME"
			Constant = s_controller
		Case "ACTION_NAME"
			Constant = s_action
		Case "__SELF__"
			Constant = Request.ServerVariables("SCRIPT_NAME")
			Dim t_s : t_s = Request.ServerVariables("QUERY_STRING")
			If t_s <> "" Then
				Constant = Constant & "?" & t_s
			End If
		End Select
		'Response.Write(Constant)
	End Property
	
	''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''模板相关方法
	
	'''变量预定义
	'''支持数组定义,数组定义和原生语法保持一致
	'''例参数"arr(5)"表示定义名为arr的一维数组,其数组大小为6(最大可用下标5)
	'''例参数"arr(5,4)"表示定义名为arr的二维数组,其数组大小为6X5
	'p_name:变量名
	Public Sub [Dim](Byval p_name)
		On Error Resume Next
		ExecuteGlobal("Dim " & p_name)
		If Err.Number<>0 Then
			Die("CORE ERROR XXX: Error while defining variable " & p_name & ".")
		End If
	End Sub
	
	'''指定当前使用的模板主题，支持链式调用
	'p_name:模板主题名称
	Public Function Theme(Byval p_name)
		s_theme = p_name
		Set Theme = Me
	End Function
	
	'''指定视图文件，支持链式调用
	'''路径使用:
	'''1:完整路径，以"/"作为开头
	'''2:当前模块模版(read)
	'''3:其他模块模板(public:header)
	'''4:其他主题模块模板(bule:user:read),不建议使用
	'''5:其他分组主题模块模板(admin:bule:user:read),不建议使用
	'p_tpl:要指定的视图文件
	Public Function View(Byval p_tpl)
		If Not Left(p_tpl, 1) = "/" Then
			Dim t_module, t_theme, t_controller, t_action
			t_module = s_module
			t_theme = s_theme
			t_controller = s_controller
			t_action = s_action
			Dim t_a1, t_a2, t_a3
			t_a1 = s_str.ParamRev(p_tpl)
			t_action = t_a1(0)
			If Has(t_a1(1)) Then
				t_a2 = s_str.ParamRev(t_a1(1))
				t_controller = t_a2(0)
				If Has(t_a2(1)) Then
					t_a3 = s_str.ParamRev(t_a2(1))
					t_theme = t_a3(0)
					If Has(t_a3(1)) Then
						t_module = t_a3(1)
					End If
				End If
			End If
			If t_theme = "" Then
				s_view = ASHAPO_APP_PATH & "/" & t_module & "/View/" & t_controller & "/" & t_action & Config("TMPL_TEMPLATE_SUFFIX")
				s_view_output_file = Lcase(t_module & "__" & t_controller & "_" & t_action & ".asp")
				s_view_output_map = Lcase(t_module & "__" & t_controller & "_" & t_action)
			Else
				s_view = ASHAPO_APP_PATH & "/" & t_module & "/View/" & t_theme & "/" & t_controller & "/" & t_action & Config("TMPL_TEMPLATE_SUFFIX")
				s_view_output_file = Lcase(t_module & "_" & t_theme & "_" & t_controller & "_" & t_action & ".asp")
				s_view_output_map = Lcase(t_module & "_" & t_theme & "_" & t_controller & "_" & t_action)
			End If
		Else
			s_view = p_tpl
			Dim t_temp
			t_temp = Replace( p_tpl, "/", "_1_" )
			t_temp = Replace( t_temp, ".", "_0_" )
			s_view_output_file = Lcase(t_temp & ".asp")
			s_view_output_map = Lcase(t_temp)
		End If
		Set View = Me
	End Function
	
	'''解析模板文件并运行
	Public Sub Display()
		If IsN(s_view) Then
			View(s_action)
		End If
		
		If Not s_fso.IsFile(s_view) Then
			Die("CORE ERROR XXX:template file [" & s_view & "] does not exist.")
		End If
		
		If Ucase(Config("TMPL_ENGINE_TYPE")) = "ASP" Then
			'''原生ASP文件
			s_asp.RunFile(s_view)
		Else
			'''使用模板引擎
			'''检测缓存是否过期
			If ASHAPO_DEBUG Then
				Console.Debug("开始检测视图缓存文件是否过期")
				If s_fso.IsFile(ASHAPO_RUNTIME_PATH & "/Cache/Map/ViewInclude/" & s_view_output_map) Then
					Dim t_str : t_str = s_fso.ReadFile(ASHAPO_RUNTIME_PATH & "/Cache/Map/ViewInclude/" & s_view_output_map)
					Dim t_map, t_maps : t_maps = Split(t_str, "|")
					
					For Each t_map In t_maps
						Dim t_enm : t_enm = Split(t_map, ":")
						If UBound(t_enm) <> 1 Then
							Console.Warn("发现意外情况的MAP个数" & t_enm)
							s_fso.DeleteFile(ASHAPO_RUNTIME_PATH & "/Cache/Map/ViewInclude/" & s_view_output_map)
							s_fso.DeleteFile(ASHAPO_RUNTIME_PATH & "/Cache/View/" & s_view_output_file)
							Exit For
						End If
						If Cstr( s_fso.GetFileDateLastModified( t_enm(0) ) ) <> t_enm(1) Then
							Console.Info("视图缓存文件过期")
							Console.Info("实际文件时间" & Cstr( s_fso.GetFileDateLastModified( t_enm(0) ) ))
							Console.Info("缓存文件时间" & Cstr( t_enm(1) ))

							s_fso.DeleteFile(ASHAPO_RUNTIME_PATH & "/Cache/Map/ViewInclude/" & s_view_output_map)
							s_fso.DeleteFile(ASHAPO_RUNTIME_PATH & "/Cache/View/" & s_view_output_file)
						End If
					Next
					
				End If
			End If
			
			If Not s_fso.IsFile(ASHAPO_RUNTIME_PATH & "/Cache/View/" & s_view_output_file) Then
				Console.Debug("视图缓存文件不存在或已过期，将重新生成")
				s_asp.Include(ASHAPO_CORE_PATH & "/Library/Driver/Template/AshapoTemplateDriver" & Config("TMPL_ENGINE_TYPE") & ".class.asp")
				s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoTemplate.class.asp")
				Dim t_tpl : Set t_tpl = New AshapoTemplate
				t_tpl.Init( Config("TMPL_ENGINE_TYPE") )
				t_tpl.SetPath(s_view)
				Call s_fso.CreateFile(ASHAPO_RUNTIME_PATH & "/Cache/View/" & s_view_output_file, t_tpl.Resolve( Config("TMPL_PARSE_STRING") ), False)
				If t_tpl.CacheMap <> "" Then
					Call s_fso.CreateFile(ASHAPO_RUNTIME_PATH & "/Cache/Map/ViewInclude/" & s_view_output_map, t_tpl.CacheMap, True)
				End If
				Set t_tpl = Nothing
			End If
			s_asp.RunFile(ASHAPO_RUNTIME_PATH & "/Cache/View/" & s_view_output_file)
		End If
	End Sub
	
	'''通用的错误页面显示
	'p_message:错误信息
	'p_jumpUrl:跳转URL
	'p_waitSecond:跳转等待时间(单位秒) todo 考虑将p_waitSecond移入配置项
	Public Sub [Error](Byval p_message, Byval p_jumpUrl, Byval p_waitSecond)
		[Dim]("message")
		message = p_message
		[Dim]("jumpUrl")
		jumpUrl = p_jumpUrl
		[Dim]("waitSecond")
		waitSecond = p_waitSecond
		View( Config("TMPL_ACTION_ERROR") )
		Display()
		Response.End()
	End Sub
	
	''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''JSON相关方法
	
	'''获取JSON单例
	Public Property Get Json()
		If Has(s_json) Then
			Set Json = s_json
		Else
			s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoJson.class.asp")
			Set s_json = New AshapoJson
			Set Json = s_json
		End If
	End Property
	
	'''输出JSON
	'p_objt:可以是JSON字符串，也可以是Json对象
	Public Sub AjaxReturn(Byval p_objt)
		Response.ContentType = "application/json"
		If Not VarType( p_objt ) = VbString Then
			p_objt = Me.Json.Encode(p_objt)
		End If
		Response.Write(p_objt)
		'Response.End()
	End Sub

	''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''入口相关方法	
	
	'''载入APP设置
	'''todo 此处还载入了公用函数
	Private Sub includeConfig_()
		Dim t_i
		'''载入公共设置
		s_asp.RunFile( ASHAPO_APP_PATH & "/Common/Conf/config.asp" )
		'''载入公共扩展设置
		If Config("LOAD_EXT_CONFIG") <> "" Then
			Dim t_cfl_c : t_cfl_c = Split(Config("LOAD_EXT_CONFIG"), ",")
			For t_i = 0 To UBound(t_cfl_c)
				s_asp.RunFile( ASHAPO_APP_PATH & "/Common/Conf/" & t_cfl_c(t_i) & ".asp" )
			Next
			'置空LOAD_EXT_CONFIG,防止接下来判断错误
			Config("LOAD_EXT_CONFIG") = ""
		End If
	
		'''载入公共函数
		If s_fso.IsFile( ASHAPO_APP_PATH & "/Common/Common/function.asp" ) Then
			s_asp.Include( ASHAPO_APP_PATH & "/Common/Common/function.asp" )
		End If
		'''载入公共扩展函数
		If Config("LOAD_EXT_FILE") <> "" Then
			Dim t_fun_c : t_fun_c = Split(Config("LOAD_EXT_FILE"), ",")
			For t_i = 0 To UBound(t_fun_c)
				s_asp.Include( ASHAPO_APP_PATH & "/Common/Common/" & t_fun_c(t_i) & ".asp" )
			Next
			'置空LOAD_EXT_FILE,防止接下来判断错误
			Config("LOAD_EXT_FILE") = ""
		End If
		
		'''载入模块设置
		If s_fso.IsFile( ASHAPO_APP_PATH & "/" & s_module & "/Conf/config.asp" ) Then
			s_asp.RunFile( ASHAPO_APP_PATH & "/" & s_module & "/Conf/config.asp" )
			'''加载模块扩展设置
			If Config("LOAD_EXT_CONFIG") <> "" Then
				Dim t_cfl_m : t_cfl_m = Split(Config("LOAD_EXT_CONFIG"), ",")
				For t_i = 0 To UBound(t_cfl_m)
					s_asp.RunFile( ASHAPO_APP_PATH & "/Common/Conf/" & t_cfl_m(t_i) & ".asp" )
				Next
				'置空LOAD_EXT_CONFIG,防止接下来判断错误
				Config("LOAD_EXT_CONFIG") = ""
			End If
		End If

		'''载入模块函数
		If s_fso.IsFile( ASHAPO_APP_PATH & "/" & s_module & "/Common/function.asp" ) Then
			s_asp.Include( ASHAPO_APP_PATH & "/" & s_module & "/Common/function.asp" )
		End If
		'''载入模块扩展函数
		If Config("LOAD_EXT_FILE") <> "" Then
			Dim t_fun_m : t_fun_m = Split(Config("LOAD_EXT_FILE"), ",")
			For t_i = 0 To UBound(t_fun_m)
				s_asp.Include( ASHAPO_APP_PATH & "/" & s_module & "/Common/" & t_fun_m(t_i) & ".asp" )
			Next
			'置空LOAD_EXT_FILE,防止接下来判断错误
			Config("LOAD_EXT_FILE") = ""
		End If
		
		'''加载完配置后要执行的动作
		s_theme = Config("DEFAULT_THEME")
	End Sub
	
	
	'''ashapo编译入口
	Public Sub Init()
		
		s_module = G(Config("VAR_MODULE") & ":s:" & Config("DEFAULT_MODULE"))
		s_controller = G(Config("VAR_CONTROLLER") & ":s:" & Config("DEFAULT_CONTROLLER"))
		s_action = G(Config("VAR_ACTION") & ":s:" & Config("DEFAULT_ACTION"))
		
		Console.SetLogPath( ASHAPO_RUNTIME_PATH & "/Logs/" & s_module & "/" & Year(Date()) & "_" & Month(Date()) & "_" & Day(Date()) & ".log" )
	
		'''s_asp.Init由于是在构建运行环境，所以必须在第一位执行
		
		s_asp.Init( Lcase( s_module & "_" & s_controller & "_" & s_action ) )
		
		Console.GroupBegin("开始执行")


		initCache_()

		initCookie_()


		'''todo 此处需要进行性能优化
		'''检查公共目录
		
		Console.Debug("载入模块设置")
		includeConfig_()
		
		s_theme = Config("DEFAULT_THEME") ''设置默认模板主题
		
		Console.Debug("载入控制器类")
		s_asp.Include( ASHAPO_APP_PATH & "/" & s_module & "/Controller/" & s_module & "Controller" & s_controller & ".class.asp")
		
		Console.Debug("开始执行动作")
		ExecuteGlobal("Dim " & Config("RUN_OBJECT_NAME"))
		ExecuteGlobal("Set " & Config("RUN_OBJECT_NAME") & " = New " & s_module & "Controller" & s_controller)
		ExecuteGlobal(Config("RUN_OBJECT_NAME") & "." & s_action & "()")
		ExecuteGlobal("Set " & Config("RUN_OBJECT_NAME") & " = Nothing")
		
		Console.GroupEnd("执行结束")
	End Sub
	
	''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''工具相关方法

	'''实例化一个自定义模型
	'''todo 待修改
	'p_name:模型名称，其中“[表名]”表示使用数据表模型，“模型名”表示根据上下文自动确定模型，“全限定名”表示使用全限定名来确定完整路径确定模型
	Public Function Model(Byval p_name)
		If Left(p_name, 1) = "[" And Right(p_name, 1) = "]" Then
			'''表模型
			s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoDatabase.class.asp")
			Dim t_tname : t_tname = Mid(p_name, 2, Len(p_name)-2)
			s_asp.Include(ASHAPO_CORE_PATH & "/Library/Driver/Model/AshapoModelDriver" & Config("DB_TYPE") & ".class.asp")
			s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoModel.class.asp")
			Set Model = (New AshapoModel).Init(Config("DB_TYPE"), Config("DB_HOST"), Config("DB_PORT"), Config("DB_NAME"), Config("DB_USER"), Config("DB_PWD") )
			Model.Prefix(Config("DB_PREFIX"))
			Model.Table(t_tname)
		ElseIf InStr(p_name, "Model") Then
			'''全限定名，如[CommonModelCity]指定模型文件
			Dim t_module : t_module = s_str.CLeft(p_name, "Model")
			Dim t_model : t_model = s_str.CRight(p_name, "Model")
			Dim t_path1 : t_path1 = ASHAPO_APP_PATH & "/" & t_module & "/Model/" & t_module & "Model" & t_model & ".class.asp"
			s_asp.Include(t_path1)
			Set Model = Eval("New " & t_module & "Model" & t_model)
		Else
			'''自动选择模型
			Dim t_path2
			t_path2 = ASHAPO_APP_PATH & "/" & s_module & "/Model/" & s_module & "Model" & p_name & ".class.asp"
			If s_fso.IsFile(t_path2) Then
				s_asp.Include(t_path2)
				Set Model = Eval("New " & s_module & "Model" & p_name)
			Else
				t_path2 = ASHAPO_APP_PATH & "/Common/Model/CommonModel" & p_name & ".class.asp"
				If s_fso.IsFile(t_path2) Then
					s_asp.Include(t_path2)
					Set Model = Eval("New CommonModel" & p_name)
				Else
					Die("找不到模型文件")
				End If
			End If
		End If
	End Function
	
	'''设置是否进行当前页面HTML静态缓存
	'todo:经调试，发现服务器需关闭服务器端调试才能正常执行
	'(由于是在本处进行静态化缓存判断，故使用时应将该语句放在所有语句的最前面，以防止动态代码的再次执行)
	'设置该属性为True时即可以对当前页面进行缓存
	'对应缓存页面应是在多次运行时无逻辑变动(因为使用HTTP进行了二次访问,当然,既然进行了HTML缓存那肯定就是因为无逻辑变动)
	'内置一个特殊的GET值,用于判断是否要进行HTML静态化(防止死循环)
	'p_b:布尔值,是否生成HTML，如果设置成False时会顺便删除已生成的HTML缓存
	Public Property Let HTML(Byval p_b)
		'HTML缓存位置
		Dim t_p : t_p = ASHAPO_RUNTIME_PATH & "/Cache/Html/" & s_module & "/" & s_controller & "/" & s_action & "/"
		Dim t_k
		'所有GET参数(不含GMAL及标识位)
		For Each t_k In Request.QueryString
			If t_k <> Config("VAR_MODULE") And t_k <> Config("VAR_CONTROLLER") And t_k <> Config("VAR_ACTION") And t_k <> Config("FITER_DISABLE_HTML") Then
				t_p = t_p & t_k & "-" & Request.QueryString(t_k) & "_"
			End If
		Next
		t_p = Left(t_p, Len(t_p)-1)
		t_p = t_p & ".asp"
		Dim t_file : t_file = t_p
		If p_b Then
			'标识位为空时则进行HTML缓存
			If Request.QueryString( Config("FITER_DISABLE_HTML") )="" Then
				If s_fso.IsFile( t_file ) Then
					'访问当前已缓存的asp运行文件
					Server.Transfer( t_file )
				Else
					s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoHttp.class.asp")
					Dim t_http : Set t_http = New AshapoHttp
					
					Dim t_url : t_url = Request.ServerVariables("SCRIPT_NAME")
					Dim t_par : t_par = Request.ServerVariables("QUERY_STRING")
					If t_par <> "" Then
						t_url = t_url & "?" & t_par & "&" & Config("FITER_DISABLE_HTML") & "=1"
					Else
						t_url = t_url & "?" & Config("FITER_DISABLE_HTML") & "=1"
					End If
					Dim t_html : t_html = t_http.Get( t_url )
					Set t_http = Nothing
					'保存到HTML文件夹
					Call s_fso.CreateFile(t_file, t_html, True)
				End If
			End If
		Else
			'''是否已有生成的HTML文件需要删除
			If s_fso.IsFile( t_file ) Then
				s_fso.DeleteFile( t_file )
			End If
		End If
	End Property

	'''简易的通用字典定义
	Public Property Get Dictionary()
		Set Dictionary = Server.CreateObject( Config.Environment("Scripting.Dictionary") )
	End Property
	
	'''初始化Sa.Cache对象
	Private Sub initCache_()
		Dim t_dirver_dir : t_dirver_dir = ASHAPO_CORE_PATH & "/Library/Driver/Cache"
		Select Case Config("CACHE_TYPE")
		Case "Application"
			s_asp.Include( t_dirver_dir & "/AshapoCacheDriverApplication.class.asp" )
		Case "File"
			s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoJson.class.asp")
			s_asp.Include( t_dirver_dir & "/AshapoCacheDriverFile.class.asp" )
		End Select
		s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoCache.class.asp")
		Set Cache = (New AshapoCache).Init( Config("CACHE_TYPE"), Config("CACHE_TIME"), Config("CACHE_PREFIX"), 0 )
	End Sub

	'''初始化Sa.Cookie对象
	Private Sub initCookie_()
		If Config("COOKIE_AES_ENCODE") Then
			s_asp.Include(ASHAPO_CORE_PATH & "/Library/Org/ClassAes.class.asp")
		End If
		s_asp.Include(ASHAPO_CORE_PATH & "/Library/Ashapo/AshapoCookie.class.asp")
		Set Cookie = New AshapoCookie
		Cookie.Expires = Config("COOKIE_EXPIRES")
		Cookie.Domain = Config("COOKIE_DOMAIN")
		Cookie.Path = Config("COOKIE_PATH")
		Cookie.Prefix = Config("COOKIE_PREFIX")
		Cookie.Secure = Config("COOKIE_SECURE")
		Cookie.HttpOnly = Config("COOKIE_HTTPONLY")
		Cookie.AesEncode = Config("COOKIE_AES_ENCODE")
		Cookie.AesKey = Config("COOKIE_AES_KEY")
	End Sub
	
	'''载入符合规则的ORG类，并返回一个实例
	'p_class_name:不含Class的类名
	Public Function Org(Byval p_class_name)
		s_asp.Include(ASHAPO_CORE_PATH & "/Library/Org/Class" & p_class_name & ".class.asp")
		Set Org = Eval("New Class" & p_class_name)
	End Function
End Class
%>